using System;
using System.Collections.Generic;
using System.IO;
using GarageGames.Torque.Core;
using GarageGames.Torque.MathUtil;
using GarageGames.Torque.SceneGraph;
using GarageGames.Torque.Sim;
using GarageGames.Torque.T3D.RigidCollision;
using GarageGames.Torque.TS;
using GarageGames.Torque.Materials;
using GarageGames.Torque.Util;
using GarageGames.Torque.XNA;
using GarageGames.Torque.RenderManager;
using GarageGames.Torque.GFX;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;



namespace GarageGames.Torque.T3D
{
    [TorqueXmlSchemaType]
    public class T3DBillboardRenderComponent : T3DRenderComponent
    {

        #region Public properties, operators, constants, and enums

        public string Filename
        {
            get;
            set;
        }

        public float Scale
        {
            get;
            set;
        }

        public float Width
        {
            get;
            set;
        }

        public float Height
        {
            get;
            set;
        }

        public bool IsSphericalBillboard
        {
            get;
            set;
        }

        #endregion


        #region Public methods

        public override void Render(SceneRenderState srs)
        {
#if DEBUG
            Profiler.Instance.StartBlock("T3DBillboardRenderComponent.Render");
#endif

            if (_mesh.IndexBuffer.IsNull || _mesh.VertexBuffer.IsNull)
            {
                _CreateMesh();
            }

            if (_mesh.IndexBuffer.IsNull || _mesh.VertexBuffer.IsNull)
                return;

            Vector3 direction = Vector3.Subtract(SceneGroup.Position, srs.CameraPosition);

            if (!IsSphericalBillboard)
                direction *= new Vector3(1, 1, 0);

            Matrix mTest = MatrixUtil.CreateAimedTransform(Vector3.Zero, direction);

            ri = SceneRenderer.RenderManager.AllocateInstance();
            ri.Type = RenderInstance.RenderInstanceType.Translucent3D;
            ri.ObjectTransform = mTest * srs.World.Top;
            ri.WorldBox = _GetWorldBox(srs.World.Top);
            ri.VertexBuffer = _mesh.VertexBuffer.Instance;
            ri.VertexSize = GFXVertexFormat.VertexSize;
            ri.VertexDeclaration = GFXVertexFormat.GetVertexDeclaration(srs.Gfx.Device);
            ri.BaseVertex = 0;
            ri.VertexCount = _mesh.VertexCount;
            ri.StartIndex = 0;
            ri.IndexBuffer = _mesh.IndexBuffer.Instance;
            ri.PrimitiveType = PrimitiveType.TriangleList;
            ri.PrimitiveCount = _mesh.TriangleCount;
            ri.UTextureAddressMode = TextureAddressMode.Wrap;
            ri.VTextureAddressMode = TextureAddressMode.Wrap;
            ri.Material = material;

            SceneRenderer.RenderManager.AddInstance(ri);

#if DEBUG
            Profiler.Instance.EndBlock("T3DBillboardRenderComponent.Render");
#endif
        }



        public override void CopyTo(TorqueComponent obj)
        {
            base.CopyTo(obj);

            T3DBillboardRenderComponent obj2 = (T3DBillboardRenderComponent)obj;
            obj2.Filename = Filename;
            obj2.Scale = Scale;
            obj2.Width = Width;
            obj2.Height = Height;
            obj2.IsSphericalBillboard = IsSphericalBillboard;
        }

        #endregion


        #region Private, protected, internal methods

        protected override bool _OnRegister(TorqueObject owner)
        {
            if (!base._OnRegister(owner))
                return false;

            return true;
        }



        protected override void _RegisterInterfaces(TorqueObject owner)
        {
            base._RegisterInterfaces(owner);

            Owner.RegisterCachedInterface("render", null, this, null);

            // cafTODO: or should we allow renaming?
            if (SceneClientName != null && SceneClientName != String.Empty)
                Owner.RegisterCachedInterface("bounds", SceneClientName, this, _objectBox);
        }



        protected override void _GetInterfaces(PatternMatch typeMatch, PatternMatch nameMatch, List<TorqueInterface> list)
        {
            if (typeMatch.TestMatch("render") && nameMatch.TestMatch(SceneGroupName))
            {
                TorqueInterfaceWrap<IRenderable3D> render = new TorqueInterfaceWrap<IRenderable3D>();
                Owner.RegisterInterface(this, render);
                list.Add(render);
            }

            base._GetInterfaces(typeMatch, nameMatch, list);
        }



        protected void _CreateMesh()
        {
            if (!_mesh.VertexBuffer.IsNull && !_mesh.IndexBuffer.IsNull)
            {
                return;
            }

            int count = 2;
            int numVerts = count * count;
            int numIndices = (count - 1) * (count - 1) * 6;
            Vector3[] verts = new Vector3[numVerts];
            short[] indices = new short[numIndices];

            Vector3 normal = -Vector3.UnitY;
            Vector3 binormal = Vector3.UnitX;
            Vector3 tangent = Vector3.UnitZ;


            Vector3 offset = new Vector3(Width / 2.0f, 0, Height / 2.0f);

            int i = 0;
            for (int x = 0; x < count; x++)
            {
                for (int y = 0; y < count; y++)
                    verts[i++] = new Vector3(((float)x / (float)(count - 1)) * Width, 0, ((float)y / (float)(count - 1)) * Height);
            }

            i = 0;
            for (int x = 0; x < count - 1; x++)
            {
                for (int y = 0; y < count - 1; y++)
                {
                    indices[i++] = (short)(count * y + x);
                    indices[i++] = (short)(count * y + x + 1);
                    indices[i++] = (short)(count * (y + 1) + x);
                    indices[i++] = (short)(count * (y + 1) + x);
                    indices[i++] = (short)(count * y + x + 1);
                    indices[i++] = (short)(count * (y + 1) + x + 1);
                }
            }

            _mesh.IndexBuffer = ResourceManager.Instance.CreateDynamicIndexBuffer(ResourceProfiles.ManualStaticIBProfile, indices.Length * sizeof(short), IndexElementSize.SixteenBits);
            _mesh.IndexBuffer.Instance.SetData<short>(indices, 0, indices.Length);
            _mesh.TriangleCount = indices.Length / 3;

            // create the new buffer of the correct size
            int sizeInBytes = verts.Length * GFXVertexFormat.VertexSize;
            _mesh.VertexBuffer = ResourceManager.Instance.CreateDynamicVertexBuffer(ResourceProfiles.ManualStaticVBProfile, sizeInBytes);
            GFXVertexFormat.PCTTBN[] vertices = TorqueUtil.GetScratchArray<GFXVertexFormat.PCTTBN>(verts.Length);

            for (i = 0; i < verts.Length; i++)
                vertices[i] = new GFXVertexFormat.PCTTBN(verts[i] - offset, Color.White, new Vector2(verts[i].X / Width, 1.0f - (verts[i].Z / Height)), Vector2.Zero, new Vector4(tangent, 0.0f), new Vector4(binormal, 0.0f));

            _mesh.VertexBuffer.Instance.SetData<GFXVertexFormat.PCTTBN>(vertices, 0, verts.Length);
            _mesh.VertexCount = verts.Length;

            material = new SimpleMaterial();
            material.IsTranslucent = true;
            material.Opacity = 0.25f;
            material.TextureFilename = Filename;
        }



        protected Box3F _GetWorldBox(Matrix transform)
        {
            return new Box3F(new Vector3(-1000000), new Vector3(1000000));
        }

        #endregion


        #region Private, protected, internal fields

        Vector3 vectorDirection = Vector3.Zero;
        Matrix matrixDirection = Matrix.Identity;
        protected RenderInstance ri;
        protected T3DSimpleMeshBase.TriangleMesh _mesh;
        SimpleMaterial material;

        #endregion
    }

}
